package com.dark.common.repository.hibernate;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

import com.dark.common.utils.ReflectionUtils;
import com.dark.common.web.model.Page;
import com.dark.common.web.model.SystemContext;
@SuppressWarnings("unchecked")
@Repository
public class HibernateDao<T, PK extends Serializable> {
	
	protected Logger logger = LoggerFactory.getLogger(getClass());
	
	@Autowired
	protected SessionFactory sessionFactory;

	protected Class<T> entityClass;
	
	@PostConstruct
	public void init() {
		this.entityClass = ReflectionUtils.getSuperGenericType(getClass());
	}
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	/**
	 * 获取当前 Session
	 * @return
	 */
	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}
	
	/**
	 * 获取对象的主键名.
	 * 
	 * @return
	 */
	public String getIdName() {
		ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
		return meta.getIdentifierPropertyName();
	}
	
	/**
	 * 保存新增对象
	 * @param entity
	 */
	public T save(T entity) {
		Assert.notNull(entity, "entity 不能为空");
		getSession().save(entity);
		logger.debug("save entity: {}", entity);
		return entity ;
	}
	
	/**
	 * 保存新增或修改的对象
	 * @param entity
	 */
	public void saveOrUpdate(T entity) {
		Assert.notNull(entity, "entity 不能为空");
		getSession().saveOrUpdate(entity);
		logger.debug("saveOrUpdate entity: {}", entity);
	}
	
	/**
	 * 按 id 获取对象
	 * @param id
	 * @return
	 */
	public T getEntity(PK id) {
		Assert.notNull(id, "id 不能为空");
		return (T) getSession().get(entityClass, id);
	}
	/**
	 * 按 id 获取对象，适用于属性注入方式
	 * @param clazz
	 * @param id
	 * @return
	 */
	public T getEntity(Class<T> clazz, PK id) {
		Assert.notNull(id, "id 不能为空");
		return (T) getSession().get(clazz, id);
	}
	
	/**
	 * 删除对象
	 * 
	 * @param entity： 持久化对象或"瞬态"对象
	 */
	public void deleteEntity(T entity) {
		Assert.notNull(entity, "entity 不能为空");
		getSession().delete(entity);
		logger.debug("delete entity: {}", entity);
	}
	public void deleteById(PK id) {
		Assert.notNull(id, "id 不能为空");
		deleteEntity(getEntity(id));
		logger.debug("delete entity {},id is {}", entityClass.getSimpleName(), id);
	}
	public void delete(Class<T> clazz, PK id) {
		Assert.notNull(id, "id 不能为空");
		deleteEntity(getEntity(clazz, id));
		logger.debug("delete entity {},id is {}", entityClass.getSimpleName(), id);
	}
	
	/**
	 * 修改对象
	 * @param entity
	 */
	public void update(T entity) {
		Assert.notNull(entity, "entity 不能为空");
		getSession().update(entity) ;
		logger.debug("update entity: {}", entity);
	}
	
	/**
	 * 执行 HQL 进行批量修改/删除操作
	 * @param queryString
	 * @param args
	 * @param alias
	 */
	public void batchExecute(String queryString, Object[] args, Map<String, Object> alias) {
		Query query = getSession().createQuery(queryString) ;
		setAliasParams(query, alias) ;
		setArgsParams(query, args) ;
		query.executeUpdate() ;
	}
	public void batchExecute(String queryString, Map<String, Object> alias) {
		this.batchExecute(queryString, null, alias) ;
	}
	public void batchExecute(String queryString, Object[] args) {
		this.batchExecute(queryString, args, null) ;
	}
	public void batchExecute(String queryString, Object arg) {
		this.batchExecute(queryString, new Object[]{arg}, null) ;
	}
	public void batchExecute(String queryString) {
		this.batchExecute(queryString, null, null) ;
	}
	
	
	
	/**
	 * 按 HQL 查询唯一对象
	 * @param queryString
	 * @param args
	 * @param alias
	 * @return
	 */
	public T findUnique(String queryString, Object[] args, Map<String, Object> alias) {
		Query query = getSession().createQuery(queryString) ;
		setAliasParams(query, alias) ;
		setArgsParams(query, args) ;
		return (T) query.uniqueResult() ;
	}
	public T findUnique(String queryString, Map<String, Object> alias) {
		return this.findUnique(queryString, null, alias) ;
	}
	public T findUnique(String queryString, Object arg) {
		return this.findUnique(queryString, new Object[]{arg}, null) ;
	}
	public T findUnique(String queryString, Object[] args) {
		return this.findUnique(queryString, args, null) ;
	}
	public T findUnique(String queryString) {
		return this.findUnique(queryString, null, null) ;
	}
	
	
	/**
	 * 按 HQL 查询对象列表
	 * @param queryString
	 * @param args
	 * @param alias
	 * @return 集合
	 */
	public List<T> findList(String queryString, Object[] args, Map<String, Object> alias) {
		Assert.hasText(queryString, "queryString不能为空");
		queryString = initSort(queryString) ;
		
		Query query = getSession().createQuery(queryString) ;
		setAliasParams(query, alias) ;
		setArgsParams(query, args) ;
		
		return query.list() ;
	}
	public List<T> findList(String queryString, Map<String, Object> alias) {
		return this.findList(queryString, null, alias);
	}
	public List<T> findList(String queryString, Object[] args) {
		return this.findList(queryString, args, null);
	}
	public List<T> findList(String queryString, Object arg) {
		return this.findList(queryString, new Object[]{arg}, null);
	}
	public List<T> findList(String queryString) {
		return this.findList(queryString, null, null) ;
	}
	
	
	/**
	 * HQL分页查询
	 * @param queryString
	 * @param args
	 * @param alias
	 * @return
	 */
	public Page<T> findPage(String queryString, Object[] args, Map<String, Object> alias) {
		Page<T> pager = new Page<T>() ;
		
		queryString = initSort(queryString) ;
		Query query = getSession().createQuery(queryString) ;
		setPageParameter(query, pager) ;
		setAliasParams(query, alias) ;
		setArgsParams(query, args) ;
		
		pager.setDataRows(query.list()) ;
		pager.setTotalNumber(this.totalCount(queryString, args, alias).intValue()) ;
		
		return pager;
	}
	public Page<T> findPage(String queryString, Map<String, Object> alias) {
		return this.findPage(queryString, null, alias);
	}
	public Page<T> findPage(String queryString, Object[] args) {
		return this.findPage(queryString, args, null) ;
	}
	public Page<T> findPage(String queryString, Object arg) {
		return this.findPage(queryString, new Object[]{arg}, null) ;
	}
	public Page<T> findPage(String queryString) {
		return this.findPage(queryString, null, null) ;
	}
	
	
	
	
	
	
	/**
	 * 按原生SQL查询一组对象
	 * @param queryString
	 * @param args
	 * @param alias
	 * @param clz
	 * @param hasEntity hasEntity 该对象是否是一个Hibernate所管理的实体
	 * @return 返回单组对象，多列
	 */
	public Object findUniqueSQL(String queryString, Object[] args, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		SQLQuery sqlQuery = getSession().createSQLQuery(queryString) ;
		setAliasParams(sqlQuery, alias) ;
		setArgsParams(sqlQuery, args) ;
		if(hasEntity) {
			sqlQuery.addEntity(clz) ;
		} else {
			sqlQuery.setResultTransformer(Transformers.aliasToBean(clz)) ;
		}
		return sqlQuery.uniqueResult() ;
	}
	public Object findUniqueSQL(String queryString, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		return this.findUniqueSQL(queryString, null, alias, clz, hasEntity) ;
	}
	public Object findUniqueSQL(String queryString, Object[] args, Class<?> clz,boolean hasEntity) {
		return this.findUniqueSQL(queryString, args, null, clz, hasEntity) ;
	}
	public Object findUniqueSQL(String queryString, Object arg, Class<?> clz, boolean hasEntity) {
		return this.findUniqueSQL(queryString, new Object[]{arg}, null, clz, hasEntity) ;
	}
	public Object findUniqueSQL(String queryString, Class<?> clz, boolean hasEntity) {
		return this.findUniqueSQL(queryString, null, null, clz, hasEntity) ;
	}
	
	
	/**
	 * 原生SQL查询一组数据
	 * @param queryString
	 * @param args
	 * @param alias
	 * @return 返回单组数据，单列
	 */
	public Object findUniqueColSQL(String queryString, Object[] args, Map<String, Object> alias) {
		SQLQuery sqlQuery = getSession().createSQLQuery(queryString) ;
		setAliasParams(sqlQuery, alias) ;
		setArgsParams(sqlQuery, args) ;
		return sqlQuery.uniqueResult() ;
	}
	public Object findUniqueColSQL(String queryString, Map<String, Object> alias) {
		return this.findUniqueColSQL(queryString, null, alias) ;
	}
	public Object findUniqueColSQL(String queryString, Object[] args) {
		return this.findUniqueColSQL(queryString, args, null) ;
	}
	public Object findUniqueColSQL(String queryString, Object arg) {
		return this.findUniqueColSQL(queryString, new Object[]{arg}, null) ;
	}
	public Object findUniqueColSQL(String queryString) {
		return this.findUniqueColSQL(queryString, null, null) ;
	}
	
	
	/**
	 * 按SQL查询，不分页，不关联对象（支持单列和多列）
	 * @param queryString 查询的SQL语言
	 * @param args 查询条件
	 * @param alias 查询条件（别名查询）
	 * @return 一组数据
	 */
	public List<Object[]> findListSQL(String queryString, Object[] args, Map<String, Object> alias) {
		SQLQuery sqlQuery = getSession().createSQLQuery(queryString) ;
		setAliasParams(sqlQuery, alias) ;
		setArgsParams(sqlQuery, args) ;
		return sqlQuery.list() ;
	}
	public List<Object[]> findListSQL(String queryString, Map<String, Object> alias) {
		return this.findListSQL(queryString, null, alias) ;
	}
	public List<Object[]> findListSQL(String queryString, Object[] args) {
		return this.findListSQL(queryString, args, null) ;
	}
	public List<Object[]> findListSQL(String queryString, Object arg) {
		return this.findListSQL(queryString, new Object[]{arg}, null) ;
	}
	
	/**
	 * 根据SQL查询对象，不包含关联对象（不分页）
	 * @param queryString 查询的SQL语言
	 * @param args 查询条件
	 * @param clz 查询的实体对象
	 * @param hasEntity 该对象是否是一个Hibernate所管理的实体，如果不是需要使用setResultTransform查询
	 * @return 一组对象
	 */
	public <N extends Object>List<N> findListSQL(String queryString, Object[] args, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		
		SQLQuery sqlQuery = this.getSession().createSQLQuery(queryString) ;
		setAliasParams(sqlQuery, alias) ;
		setArgsParams(sqlQuery, args) ;
		if(hasEntity) {
			sqlQuery.addEntity(clz) ;
		} else {
			sqlQuery.setResultTransformer(Transformers.aliasToBean(clz)) ;
		}
		return sqlQuery.list() ;
		
	}
	public <N extends Object>List<N> findListSQL(String queryString, Map<String, Object> alias, Class<?> clz,
			boolean hasEntity) {
		return this.findListSQL(queryString, null, alias, clz, hasEntity) ;
	}
	public <N extends Object>List<N> findListSQL(String queryString, Object[] args, Class<?> clz, boolean hasEntity) {
		return this.findListSQL(queryString, args, null, clz, hasEntity) ;
	}
	public <N extends Object>List<N> findListSQL(String queryString, Object arg, Class<?> clz, boolean hasEntity) {
		return this.findListSQL(queryString, new Object[]{arg}, null, clz, hasEntity) ;
	}
	public <N extends Object>List<N> findListSQL(String queryString, Class<?> clz, boolean hasEntity) {
		return this.findListSQL(queryString, null, null, clz, hasEntity) ;
	}
	
	
	/**
	 * 按原生SQL查询，并分页
	 * @param queryString
	 * @param args
	 * @param alias
	 * @param clz
	 * @param hasEntity hasEntity 该对象是否是一个Hibernate所管理的实体
	 * @return
	 */
	public <N extends Object>Page<N> findPageSQL(String queryString, Object[] args, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		Page<N> pager = new Page<N>() ;
		
		queryString = initSort(queryString) ; 
		SQLQuery sqlQuery = getSession().createSQLQuery(queryString) ;
		setAliasParams(sqlQuery, alias) ;
		setArgsParams(sqlQuery, args) ;
		setPageParameter(sqlQuery, pager) ;
		if(hasEntity) {
			sqlQuery.addEntity(clz) ;
		} else {
			sqlQuery.setResultTransformer(Transformers.aliasToBean(clz)) ;
		}
		pager.setDataRows(sqlQuery.list()) ;
		pager.setTotalNumber(this.totalCountSQL(queryString, args, alias).intValue()) ;
		
		return pager;
	}
	public <N extends Object>Page<N> findPageSQL(String queryString, Map<String, Object> alias, Class<?> clz, boolean hasEntity) {
		return this.findPageSQL(queryString, null, alias, clz, hasEntity);
	}
	public <N extends Object>Page<N> findPageSQL(String queryString, Object[] args, Class<?> clz, boolean hasEntity) {
		return this.findPageSQL(queryString, args, null, clz, hasEntity);
	}
	public <N extends Object>Page<N> findPageSQL(String queryString, Object arg, Class<?> clz,
			boolean hasEntity) {
		return this.findPageSQL(queryString, new Object[]{arg}, null, clz, hasEntity);
	}
	public <N extends Object>Page<N> findPageSQL(String queryString, Class<?> clz, boolean hasEntity) {
		return this.findPageSQL(queryString, null, null, clz, hasEntity);
	}
	
	
	
	/**
	 * 原生SQL执行
	 * @param queryString
	 * @param args
	 * @return 返回执行的有效行
	 */
	public int executeSQL(String queryString, Object[] args, Map<String, Object> alias) {
		SQLQuery sqlQuery = getSession().createSQLQuery(queryString) ;
		setAliasParams(sqlQuery, alias) ;
		setArgsParams(sqlQuery, args) ;
		return sqlQuery.executeUpdate() ;
	}
	public int executeSQL(String queryString, Map<String, Object> alias) {
		return this.executeSQL(queryString, null, alias);
	}
	public int executeSQL(String queryString, Object[] args) {
		return this.executeSQL(queryString, args, null);
	}
	public int executeSQL(String queryString, Object arg) {
		return this.executeSQL(queryString, new Object[]{arg}, null);
	}
	public int executeSQL(String queryString) {
		return this.executeSQL(queryString, null, null);
	}
	
	
	public Long totalCount(String queryString, Object[] args, Map<String, Object> alias) {
		queryString = getCountSQL(queryString, true) ;
		Query query = getSession().createQuery(queryString) ;
		setAliasParams(query, alias) ;
		setArgsParams(query, args) ;
		return (Long) query.uniqueResult() ;
	}
	public Long count(String queryString, Object[] args) {
		return this.totalCount(queryString, args, null) ;
	}
	public Long count(String queryString, Map<String, Object> alias) {
		return this.totalCount(queryString, null, alias) ;
	}
	public Long count(String queryString) {
		return this.totalCount(queryString, null, null);
	}
	
	
	public BigInteger totalCountSQL(String queryString, Object[] args, Map<String, Object> alias) {
		queryString = getCountSQL(queryString, false) ;
		SQLQuery sqlQuery = getSession().createSQLQuery(queryString) ;
		setAliasParams(sqlQuery, alias) ;
		setArgsParams(sqlQuery, args) ;
		return (BigInteger)sqlQuery.uniqueResult();
	}
	public BigInteger countSQL(String queryString, Object[] args) {
		return this.totalCountSQL(queryString, args, null) ;
	}
	public BigInteger countSQL(String queryString, Map<String, Object> alias) {
		return this.totalCountSQL(queryString, null, alias) ;
	}
	public BigInteger countSQL(String queryString) {
		return this.totalCountSQL(queryString, null, null) ;
	}
	
	
	/**
	 * 排序
	 * @param queryString
	 * @return
	 */
	private String initSort(String queryString) {
		String sort = SystemContext.getSort() ;
		String order = SystemContext.getOrder() ;
		if(sort != null && !"".equals(sort.trim())) {
			queryString += " order by " + sort ;
			if(null == order || "".equals(order)) queryString += " desc" ;
		}
		return queryString ;
	}
	
	/**
	 * 设置分页参数
	 * @param query
	 * @param pager
	 */
	private void setPageParameter(Query query, Page<?> pager) {
		Integer currentPage = SystemContext.getCurrentPage() ;
		Integer pageNumber = SystemContext.getPageNumber() ;
		
		if(pageNumber != -1) {
			if(currentPage == null || currentPage < 0) currentPage = 1 ;
			if(pageNumber == null || pageNumber < 0) pageNumber = 30 ;
			pager.setCurrentPage(currentPage) ;
			pager.setPageNumber(pageNumber) ;
			query.setFirstResult(currentPage).setMaxResults(pageNumber) ;
		}
		
	}
	
	/**
	 * 根据查询 HQL 与参数列表创建 Query 对象
	 * @param query
	 * @param alias
	 */
	private void setAliasParams(Query query, Map<String, Object> alias) {
		if(alias != null) {
			Set<String> keys = alias.keySet() ;
			for (String key : keys) {
				Object val = alias.get(key) ;
				
				if(val instanceof Integer[]) {
					//查询条件是列表
					query.setParameterList(key, (Integer[])val) ;
				} else if(val instanceof String[]) {
					//查询条件是列表
					query.setParameterList(key, (String[])val) ;
				} else if(val instanceof List) {
					//查询条件是列表
					query.setParameterList(key, (List<Object>)val) ;
				} else if(val instanceof Collection) {
					//查询条件是列表
					query.setParameterList(key, (Collection<?>)val) ;
				} else {
					query.setParameter(key, val);
				}
			}
		}
	}
	
	/**
	 * 根据查询 HQL 与参数列表创建 Query 对象
	 * @param query
	 * @param values:数来那个可变的参数, 按顺序绑定
	 */
	private void setArgsParams(Query query, Object... values) {
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
	}
	
	
	private String getCountSQL(String queryString, boolean isHQL) {
		String e = queryString.substring(queryString.toLowerCase().lastIndexOf("from")) ;
		String c = "select count(*) " + e ;
		if(isHQL) c.replaceAll("fetch", "") ;
		return c ;
	}
	
	
	
	
	
	
	
	
	

	private String getEntityName(Class<T> entityClass) {
		//得到模型对象的实体名
		return getSessionFactory().getClassMetadata(entityClass).getEntityName();
	}
	public List<T> listAll(Class<T> entityClass) {
		//根据实体名生成HQL
		String HQL_LIST_ALL = "from " + getEntityName(entityClass);
		return this.getSession().createQuery(HQL_LIST_ALL).list() ;
	}
	
	public T countAll(Class<T> entityClass) {
		//根据实体名生成HQL
		String HQL_COUNT_ALL = "select count(*) from " + getEntityName(entityClass);
		T uniqueResult = (T) this.getSession().createQuery(HQL_COUNT_ALL).uniqueResult() ;
		return uniqueResult  ;
	}
	
	public Page<T> listAll(Class<T> entityClass, int currentPage, int pageNumber) {
		SystemContext.setCurrentPage(currentPage) ;
		SystemContext.setPageNumber(pageNumber) ;
		//根据实体名生成HQL
		String queryString = "from " + getEntityName(entityClass);
		return this.findPage(queryString)  ;
    }
	
	
	
	
	
	
	
	
	
	
	
	
	
	
}
